Skip to content

feat(api): MCP OAuth discovery on /mcp (RFC-0044 phase 1) - #152

Merged
Madraka merged 5 commits into
mainfrom
feat/mcp-oauth-discovery
Nov 7, 2026
Merged

Madraka merged 5 commits into
mainfrom
feat/mcp-oauth-discovery

Conversation

@Madraka

@Madraka Madraka commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

What

Phase 1 of RFC-0044 (#144, accepted 2026-11-08): OAuth discovery for remote MCP clients, behind a new McpMountOptions.oauth option (issuer, resource, optional scopesSupported, resourceName, documentation).

  • /mcp 401 answers WWW-Authenticate: Bearer resource_metadata="…/.well-known/oauth-protected-resource/mcp", scope="…" (RFC 9728 §5.1, RFC 6750), with error="invalid_token" when a bearer was presented. The JSON body is unchanged.
  • The RFC 9728 document is served at the path-aware form and at the root (the fallback clients probe), cacheable for an hour; scopes_supported is scopesForMode("edits"), the minimal set, as the RFC decides.
  • Step-up: a token principal (new optional Principal.grantId, set by phase 2) calling a tool whose scopes the token lacks but the member's role holds gets 403 insufficient_scope with every missing scope in one challenge, decided from the static tool policy table before the JSON-RPC handler runs. Keys are never challenged (they cannot re-authorize), nor is a scope the role lacks (the client would loop).
  • The /mcp route moved from createApi to packages/api/src/mcp-mount.ts; createApi shrank from 217 to 188 lines and the size baseline is tightened.
  • Shared suite src/testing-mcp-oauth.ts on better-sqlite3 and Miniflare D1 (test/mcp-oauth.test.ts): challenge, invalid_token, both documents, bare 401 without the option, step-up for a grant principal, no challenge for a Contributor's grant or for an agent key. route-scopes.test.ts declares the two well-known routes.
  • ADR-0004 addendum: "No OAuth provider" now points at RFC-0044. Changeset: patch for @cloudhal/api and @cloudhal/agent-runtime.

Not wired into the admin Worker or cloudhal studio yet: a deployment that sets the option would advertise an issuer whose metadata does not exist until phase 2 (the Better Auth authorization server). Phase 2 also needs cloudhal studio --base-url (#143).

Checklist

  • pnpm check and cargo test --workspace pass locally — ran pnpm lint (Biome 2.6.2, error level), tsc --noEmit for @cloudhal/api and @cloudhal/agent-runtime, node scripts/size-check.ts, and vitest run test/mcp-oauth.test.ts test/route-scopes.test.ts test/agent-key-auth.test.ts (24 tests, both backends). The full suites were not run on the owner's machine by request; CI runs them.
  • Contract change? pnpm gen was run and generated files are committed (docs/adr/0001) — n/a
  • User-visible change in a published package? pnpm changeset added
  • Commits are signed off (git commit -s, DCO)
  • Docs / AGENTS.md updated if behaviour or commands changed — ADR-0004 addendum; AGENTS.md unchanged (no command changed)

🤖 Generated with Claude Code

Madraka and others added 5 commits November 8, 2026 01:57
With `McpMountOptions.oauth` (issuer, resource) the 401 of /mcp carries a
WWW-Authenticate challenge naming the RFC 9728 protected resource metadata
and the scopes to request, the metadata is served at both well-known forms,
and a token principal (`Principal.grantId`, phase 2) calling a tool without
a scope its member's role holds gets a 403 insufficient_scope step-up
challenge before the handler runs. The /mcp route moves to mcp-mount.ts;
createApi shrinks and the size baseline is tightened. Shared suite on
better-sqlite3 and Miniflare D1; ADR-0004 addendum points at RFC-0044.

Refs #144

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
The docs site publishes only pages on the branch; the RFC file lands with #146.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
RFC-0044 decision 10: the playground stays out of OAuth, so the two
discovery routes are on the deny list (block "mcp").

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
The RFC file is on main since #146.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
@Madraka
Madraka merged commit f115854 into main Nov 7, 2026
5 checks passed
@Madraka
Madraka deleted the feat/mcp-oauth-discovery branch November 7, 2026 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant